package com.lagou.rpc.provider.handler;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.provider.anno.RpcService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 业务处理类
 * 1.将标有@RpcService注解的bean缓存
 * 2.接收客户端请求
 * 3.根据传递过来的beanName从缓存中查找到对应的bean
 * 4.解析请求中的方法名称. 参数类型 参数信息
 * 5.反射调用bean的方法
 * 6.给客户端进行响应
 * @author: yx
 * @date: 2022/1/11 13:49
 */
@Component
@ChannelHandler.Sharable//变成可共享的通道
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    /**
     * 缓存bean
     */
    private static final Map SERVER_INSTANCE_MAP = new ConcurrentHashMap();


    /**
     * 1.将标有@RpcService注解的bean缓存
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> rpcServiceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if (!CollectionUtils.isEmpty(rpcServiceMap)) {
            for (Map.Entry<String, Object> entry : rpcServiceMap.entrySet()) {
                Object serviceBean = entry.getValue();
                Class<?>[] interfaces = serviceBean.getClass().getInterfaces();
                if (interfaces.length == 0) {
                    throw new RuntimeException("服务必须实现接口");
                }
                //默认获取第一个接口作为缓存bean的名称
                String name = interfaces[0].getName();
                SERVER_INSTANCE_MAP.put(name, serviceBean);
            }
        }
    }


    /**
     * 通道读取就绪事件
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("接受到的消息: " + msg);
        //1.接收客户端请求 将msg转换为json对象
        RpcRequest rpcRequest = JSON.parseObject(msg, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());
        //2.执行本地方法
        try {
            rpcResponse.setResult(handler(rpcRequest));
        } catch (Exception e) {
            e.printStackTrace();
            rpcResponse.setError(e.getMessage());
        }
        //6.给客户端响应
        ctx.writeAndFlush(JSON.toJSONString(rpcResponse));
    }

    /**
     * 执行本地方法
     *
     * @param rpcRequest
     * @return
     * @throws InvocationTargetException
     */
    private Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        //3.根据传来的className获取缓存中对于的bean
        String className = rpcRequest.getClassName();
        Object serviceBean = SERVER_INSTANCE_MAP.get(className);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("找不对应的的服务, %s", className));
        }
        //4.解析请求中的方法名称.参数类型 参数信息
        Class<?> serviceBeanClass = serviceBean.getClass();
        String methodName = rpcRequest.getMethodName();
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        Object[] parameters = rpcRequest.getParameters();
        //5.CGLIB反射调用方法
        FastClass fastClass = FastClass.create(serviceBeanClass);
        //获取到对应的方法
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);
        Object result = method.invoke(serviceBean, parameters);
        return result;
    }

}
